/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * RSTechnique.cpp
 *
 *  Created on: 2010/03/08
 *      Author: Alexandre Pires ( c.alexandre.pires@gmail.com )
 */

#include "RSTechnique.h"
#include "RSTechniqueElement.h"
#include <QTextStream>

namespace Realsphere {

    RSTechnique::RSTechnique ( RSTechniqueProfileType rTechniqueProfileType,
            const QString& rProfile, const QString& rXMLNS ) :
        RSElement ( RSTechnique::TechniqueProfileTypeToTag (
                rTechniqueProfileType ) )
    {
        mTechniqueProfileType = rTechniqueProfileType;
        mProfile = rProfile;
        mXMLNS = rXMLNS;
    }

    RSTechnique::~RSTechnique ()
    {
        mTechniqueElements.clear ();
    }

    RSTechniqueProfileType RSTechnique::TechniqueProfileType () const
    {
        return mTechniqueProfileType;
    }

    QString RSTechnique::Profile () const
    {
        return mProfile;
    }

    QString RSTechnique::XMLNS () const
    {
        return mXMLNS;
    }

    RSTechniqueElement* RSTechnique::GetTechniqueElement ( unsigned int rIndex ) const
    {
        return mTechniqueElements[rIndex];
    }

    RSTechniqueElement* RSTechnique::GetTechniqueElement (
            const QString& rScopeId ) const
    {
        RSTechniqueElementsIterator iterator = RSTechniqueElementsIterator (
                mTechniqueElements );

        while ( iterator.hasNext () )
        {
            RSTechniqueElement* technique = iterator.next ();
            if ( technique->ScopeId () == rScopeId )
                return technique;
        }

        return NULL;
    }

    RSTechniqueElements RSTechnique::TechniqueElements () const
    {
        return mTechniqueElements;
    }

    RSTechniqueElementsIterator RSTechnique::TechniqueElementsIterator () const
    {
        return RSTechniqueElementsIterator ( mTechniqueElements );
    }

    int RSTechnique::IndexOf ( RSTechniqueElement* rTechniqueElement )
    {
        return mTechniqueElements.indexOf ( rTechniqueElement );
    }

    unsigned int RSTechnique::TechniqueElementsSize ()
    {
        return mTechniqueElements.size ();
    }

    bool RSTechnique::IsRootElement () const
    {
        return false;
    }

    void RSTechnique::SetProfile ( const QString& rProfile, bool rEmit )
    {
        mProfile = rProfile;

        if ( rEmit )
            emit ProfileChanged ( mProfile );
    }

    void RSTechnique::SetXMLNS ( const QString& rXMLNS, bool rEmit )
    {
        mXMLNS = rXMLNS;

        if ( rEmit )
            emit XMLNSChanged ( mXMLNS );
    }

    RSError RSTechnique::AddTechniqueElement (
            RSTechniqueElement* rTechniqueElement )
    {
        RSError error = AddChildElement ( rTechniqueElement );
        return error;
    }

    RSError RSTechnique::RemoveTechniqueElement (
            RSTechniqueElement* rTechniqueElement )
    {
        RSError error = RemoveChildElement ( rTechniqueElement );
        return error;
    }

    RSError RSTechnique::RemoveTechniqueElement ( unsigned int rIndex )
    {
        RSTechniqueElement* technique = GetTechniqueElement ( rIndex );
        RSError error = RemoveTechniqueElement ( technique );
        return error;
    }

    RSError RSTechnique::RemoveTechniqueElement ( const QString& rScopeId )
    {
        RSTechniqueElement* technique = GetTechniqueElement ( rScopeId );
        RSError error = RemoveTechniqueElement ( technique );
        return error;
    }

    RSError RSTechnique::BeforeAddChildElement ( RSElement* rChild )
    {
        RSError error = RSElement::BeforeAddChildElement ( rChild );

        if ( error == RS_INVALID_ELEMENT )
        {
            if ( rChild->InstanceOf ( RS_TECHNIQUE_ELEMENT_CLASS ) )
            {
                RSTechniqueElement* technique_element =
                        RSTechniqueElementCast ( rChild );

                RSTechniqueElement* scope_id_technique = GetTechniqueElement (
                        technique_element->ScopeId () );

                if ( scope_id_technique == NULL )
                {
                    error = RS_NO_ERROR;
                }
                else
                {
                    error = RS_SID_ALREADY_TAKEN;
                }
            }
        }

        return error;
    }

    bool RSTechnique::AfterAddChildElement ( RSElement* rChild )
    {
        bool handled = RSElement::AfterAddChildElement ( rChild );

        if ( !handled )
        {
            if ( rChild->InstanceOf ( RS_TECHNIQUE_ELEMENT_CLASS ) )
            {
                RSTechniqueElement* technique_element =
                        RSTechniqueElementCast ( rChild );
                mTechniqueElements.append ( technique_element );
                connect ( technique_element, SIGNAL ( TechniqueElementChanged() ), this, SIGNAL ( TechniqueChanged () ) );
                handled = true;

                emit TechniqueElementAdded ( technique_element );
            }
        }

        return handled;
    }

    bool RSTechnique::AfterRemoveChildElement ( RSElement* rChild )
    {

        bool handled = RSElement::AfterRemoveChildElement ( rChild );

        if ( !handled )
        {
            if ( rChild->InstanceOf ( RS_TECHNIQUE_ELEMENT_CLASS ) )
            {
                RSTechniqueElement* technique_element =
                        RSTechniqueElementCast ( rChild );

                int technique_index = mTechniqueElements.indexOf (
                        technique_element );

                mTechniqueElements.removeAt ( technique_index );
                disconnect ( technique_element, SIGNAL ( TechniqueElementChanged() ), this, SIGNAL ( TechniqueChanged () ) );
                handled = true;

                emit TechniqueElementRemoved ( technique_element );
            }
        }

        return handled;
    }

    QString RSTechnique::TechniqueProfileTypeToTag (
            RSTechniqueProfileType rTechniqueProfileType )
    {

        switch ( rTechniqueProfileType )
        {
            case RS_TECHNIQUE_SPECIFIC:
                return QString ( RS_TECHNIQUE_TAG );
                break;
            case RS_TECHNIQUE_COMMON:
                return QString ( RS_TECHNIQUE_COMMON_TAG );
                break;
            default:
                return QString ( RS_ELEMENT_TAG );
                break;
        }
    }

    RSTechniqueProfileType RSTechnique::TechniqueProfileTagToType (
            const QString& rTag )
    {

        if ( rTag == RS_TECHNIQUE_TAG )
        {
            return RS_TECHNIQUE_SPECIFIC;
        }
        else if ( rTag == RS_TECHNIQUE_COMMON_TAG )
        {
            return RS_TECHNIQUE_COMMON;
        }

        return RS_TECHNIQUE_UNKNOWN;
    }

}
